﻿using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;

namespace SimpleORM
{
    public interface IProviderDbCommon : IProviderDatabase
    {
        #region Methods

        #endregion
    }
    public abstract class ProviderOpenable : Provider, IProviderOpenable
    {
        #region Properties

        public virtual bool IsOpen { get; protected set; }

        #endregion

        #region Fields

        #region Fields - Connection

        private int cntOpen = 0;

        #endregion

        #endregion

        #region Ctor

        public ProviderOpenable(string cntString)
            : base(cntString)
        {
        }

        #endregion

        #region Methods

        #region Methods - Public

        public bool Open()
        {
            lock (this)
            {
                bool success = cntOpen == 0;

                cntOpen++;

                if (cntOpen == 1)
                    success = OnOpen();

                return success;
            }
        }

        public bool Close()
        {
            lock (this)
            {
                bool success = cntOpen == 1;

                if (cntOpen == 1)
                    success = OnClose();

                cntOpen--;
                if (cntOpen < 0)
                    cntOpen = 0;

                return success;
            }
        }

        #endregion

        #region Methods - Virtual

        #region Methods - Virtual - Function

        protected virtual bool OnOpen()
        {
            return false;
        }

        protected virtual bool OnClose()
        {
            return false;
        }

        #endregion

        #endregion

        #endregion
    }
    public abstract class ProviderDatabase : ProviderOpenable, IProviderDatabase
    {
        #region Properties

        public virtual bool IsTransactionBegin { get; protected set; }
        public virtual bool IsSupportBatch { get { return true; } }
        public virtual bool IsSupportSchema { get { return true; } }

        #endregion

        #region Fields

        private int cntTransaction = 0;

        #endregion

        #region Ctor

        public ProviderDatabase(string cntString)
            : base(cntString)
        {
        }

        #endregion

        #region Methods

        #region Methods - Transaction

        public bool BeginTransaction()
        {
            lock (this)
            {
                bool success = cntTransaction == 0;

                cntTransaction++;

                if (cntTransaction == 1)
                    success = OnBeginTransaction();

                return success;
            }
        }

        public bool CommitTransaction()
        {
            lock (this)
            {
                bool success = cntTransaction == 1;

                if (cntTransaction == 1)
                    success = OnCommit();

                cntTransaction--;
                if (cntTransaction < 0)
                    cntTransaction = 0;

                return success;
            }
        }

        public bool RollbackTransaction()
        {
            lock (this)
            {
                bool success = cntTransaction == 1;

                if (cntTransaction == 1)
                    success = OnRollback();

                cntTransaction--;
                if (cntTransaction < 0)
                    cntTransaction = 0;

                return success;
            }
        }

        protected virtual bool OnBeginTransaction()
        {
            return false;
        }

        protected virtual bool OnCommit()
        {
            return false;
        }

        protected virtual bool OnRollback()
        {
            return false;
        }

        #endregion

        #region Methods - Build

        public virtual IDCommand BuildScript(ICommandContext context)
        {
            return null;
        }

        public virtual IDCommand BuildProcedure(ICommandContext context)
        {
            return null;
        }

        #endregion

        #region Methods - Component

        public virtual ExpressionTranslater CreateExpressionTranslater()
        {
            return null;
        }

        public virtual IScriptFactory CreateScriptFactory()
        {
            return null;
        }

        public virtual DReader CreateDataReader()
        {
            return null;
        }

        public virtual IDbSchema CreateSchema()
        {
            return null;
        }

        #endregion

        #endregion


    }
    public abstract class ProviderDbCommon : ProviderDatabase, IProviderDbCommon
    {
        #region Properties

        public DbConnection Connection { get; private set; }
        public DbTransaction Transaction { get; private set; }

        public override bool IsTransactionBegin
        {
            get { return Transaction != null; }
        }

        public override bool IsOpen
        {
            get { return Connection.State == ConnectionState.Open; }
        }

        #endregion

        #region Ctor

        public ProviderDbCommon(string cntString)
            : base(cntString)
        {
        }

        #endregion

        #region Methods

        #region Methods - Connection

        protected virtual DbConnection OnCreateConnection(string cntString)
        {
            return null;
        }

        protected override bool OnOpen()
        {
            Connection.Open();
            return true;
        }

        protected override bool OnClose()
        {
            Connection.Close();
            return true;
        }

        protected override Exception OnTest()
        {
            try
            {
                Connection.Open();
                Connection.Close();
            }
            catch (Exception ex)
            {
                return ex;
            }

            return null;
        }

        #endregion

        #region Methods - Transaction

        protected override bool OnBeginTransaction()
        {
            Transaction = Connection.BeginTransaction(IsolationLevel.ReadUncommitted);
            return true;
        }

        protected override bool OnCommit()
        {
            Transaction.Commit();
            Transaction = null;
            return true;
        }

        protected override bool OnRollback()
        {
            Transaction.Rollback();
            Transaction = null;
            return true;
        }

        #endregion

        #region Methods - Build

        public override IDCommand BuildScript(ICommandContext context)
        {
            DbCommand cmd = OnCreateCommand();
            cmd.Connection = Connection;
            cmd.Transaction = Transaction;
            cmd.CommandText = context.CommandText.ToString();
            cmd.CommandType = CommandType.Text;

            IDCommand dcmd = OnCreateCommand(cmd);
            AddParameter(dcmd, context.Parameters.ToArray());

            return dcmd;
        }

        public override IDCommand BuildProcedure(ICommandContext context)
        {
            DbCommand cmd = OnCreateCommand();
            cmd.Connection = Connection;
            cmd.Transaction = Transaction;
            cmd.CommandText = context.CommandText.ToString();
            cmd.CommandType = CommandType.StoredProcedure;

            IDCommand dcmd = OnCreateCommand(cmd);
            AddParameter(dcmd, context.Parameters.ToArray());

            return dcmd;
        }

        #endregion

        #region Methods - Command

        protected virtual DbCommand OnCreateCommand()
        {
            return null;
        }

        protected virtual IDCommand OnCreateCommand(DbCommand cmd)
        {
            return new DbCCommand(cmd);
        }

        #endregion

        #region Methods - Compoennt

        public override ExpressionTranslater CreateExpressionTranslater()
        {
            return new ExpressionToSql() { DataSource = this };
        }

        public override IScriptFactory CreateScriptFactory()
        {
            return new SqlFactory();
        }

        public override DReader CreateDataReader()
        {
            return new DbCReader();
        }

        #endregion

        #region Methods - Override

        protected override void OnInitializeComponent(string cntString)
        {
            Connection = OnCreateConnection(cntString);
            Connection.ConnectionString = cntString;
        }

        public override void Dispose()
        {
            base.Dispose();

            Transaction = null;
            Connection = null;
        }

        #endregion

        #region Methods - Private

        private void AddParameter(IDCommand cmd, DParameter[] parameters)
        {
            foreach (DbCParameter param in parameters)
                cmd.Parameters.Add(param.Instance);
        }

        #endregion

        #endregion
    }
    public interface IProviderDbCSQLServer : IProviderDbCommon
    {
    }
    public class ProviderDbCSQLServer : ProviderDbCommon, IProviderDbCSQLServer
    {
        #region Properties

        #endregion

        #region Ctor

        public ProviderDbCSQLServer(string cntString)
            : base(cntString)
        {
        }

        #endregion

        #region Methods

        #region Methods - Override

        protected override DbConnection OnCreateConnection(string cntString)
        {
            return new SqlConnection();
        }

        protected override DbCommand OnCreateCommand()
        {
            return new SqlCommand();
        }

        public override IScriptFactory CreateScriptFactory()
        {
            return new SqlFactorySqlServer();
        }

        public override DReader CreateDataReader()
        {
            return new DbCSQLServerReader();
        }

        public override IDbSchema CreateSchema()
        {
            return new SqlServerSchema(this);
        }

        public override ExpressionTranslater CreateExpressionTranslater()
        {
            return new ExpressionToSqlSQLServer() { DataSource = this };
        }

        #endregion

        #endregion
    }
}
